package org.rost.web.thrithon.core;

import java.io.*;
import java.net.*;
import java.util.*;
import java.text.*;
import javax.servlet.*;
import javax.servlet.http.*;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

public class ThrithonPathFilter implements Filter {

    private FilterConfig filterConfig = null;

    public ThrithonPathFilter() {
    } 

    private void doProcessing(HttpServletRequest req, 
            HttpServletResponse response, FilterChain chain)
	throws IOException, ServletException {
//        EngineAppContext.log("getContextPath = "+req.getContextPath());
//        EngineAppContext.log("getServletPath = "+req.getServletPath());
        try {

            ThrithonRequestContext ctx = new ThrithonRequestContext();
            req.setAttribute("EngineContext", ctx);
            ctx.setContextPath(req.getContextPath());
            ctx.setServletPath(req.getServletPath());
            ctx.setRequest(req);
            ctx.setResponse(response);
            ctx.setStream(response.getOutputStream());
            //Path parsing

            String path = req.getServletPath();
            if ("".equals(path)) {
                path = "/";
            }
            boolean isAction = false;
            String action = null;
            if(path.startsWith(ThrithonAppContext.getContext().getActionPrefix())){
                isAction = true;
                path = path.substring(ThrithonAppContext.getContext().getActionPrefix().length()+1);
                if(path.indexOf("/")!=-1){
                    action = path.substring(0, path.indexOf("/"));
                    path = path.substring(path.indexOf("/"));
                }else{
                    action = path;
                    path = "";
                }
            }
            
            if (path.endsWith("/")) {
                path += ThrithonAppContext.getContext().getDefaultPage();
            }
//            EngineAppContext.log("Path = " + path);
            if(!"".equals(path)){
                boolean ok = false;
                for (String ext : ThrithonAppContext.getContext().getExtensions()) {
                    if (path.endsWith(ext)) {
                        path = path.substring(0, path.length() - ext.length());
                        ok = true;
                        break;
                    }
                }
                if (!ok) {
                    chain.doFilter(req, response);
                    return;
                }
            }
//            EngineAppContext.log("Handling " + path);
            StringTokenizer st = new StringTokenizer(path, "/");
            String template = "";
            String currentDir = "";
            for(int i = 0; st.hasMoreElements(); i++) {
                String item = st.nextToken();
                if("p".equals(item)){
                    for (int j = 0; st.hasMoreElements(); j++) {
                        String p = st.nextToken();
                        ctx.getParameters().add(p);
                    }
                    break;
                }else{
                    currentDir = template;
                    template += ("/"+item);
                }
            }
            currentDir += "/";
            template += ".xml";
            ctx.setTemplate(template);
            ctx.setCurrentDir(currentDir);
//            EngineAppContext.log("Template: "+template+", params: "+ctx.getParameters().size()+
//                    ", action = "+action+", curDir = "+currentDir);
            try {
                
                for (FilterDefinition filterDefinition : ThrithonAppContext.getContext().getFilters()) {
                    if(filterDefinition.isBeforeAction())
                        filterDefinition.getFilter().processing(ctx);
                }
                if(action!=null){
                    ctx.callAction(action);
                }
                for (FilterDefinition filterDefinition : ThrithonAppContext.getContext().getFilters()) {
                    if(!filterDefinition.isBeforeAction())
                        filterDefinition.getFilter().processing(ctx);
                }
            } catch (ThrithonException e) {
                ThrithonAppContext.log(e.toString());
            }
            for (int i = ThrithonAppContext.getContext().getFilters().size()-1; i >= 0; i--) {
                ThrithonFilter filter = ThrithonAppContext.getContext().getFilters().get(i).getFilter();
                filter.postprocessing(ctx);
            }
        } catch (Throwable t) {
            t.printStackTrace();
            ThrithonAppContext.log(t.toString());
        }    
    } 

    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain)
	throws IOException, ServletException {
	doProcessing((HttpServletRequest)request, 
                (HttpServletResponse)response, chain);	
    }

    
    /**
     * Return the filter configuration object for this filter.
     */
    public FilterConfig getFilterConfig() {
	return (this.filterConfig);
    }


    /**
     * Set the filter configuration object for this filter.
     *
     * @param filterConfig The filter configuration object
     */
    public void setFilterConfig(FilterConfig filterConfig) {

	this.filterConfig = filterConfig;
    }

    /**
     * Destroy method for this filter 
     *
     */
    public void destroy() { 
    }


    /**
     * Init method for this filter 
     *
     */
    public void init(FilterConfig filterConfig) { 

	this.filterConfig = filterConfig;
        try {
            ThrithonAppContext.getContext().setServletContext(filterConfig.getServletContext());
            
            XMLDocumentBuilder xml = new XMLDocumentBuilder();
            xml.readXML(filterConfig.getServletContext().getResourceAsStream("/WEB-INF/thrithon.xml"));
            ThrithonAppContext.getContext().setDefaultPage(xml.getRootElement().
                    getElementsByTagName("defaultPage").item(0).
                    getFirstChild().getTextContent());
            ThrithonAppContext.getContext().setActionPrefix(xml.getRootElement().
                    getElementsByTagName("actionPrefix").item(0).
                    getFirstChild().getTextContent());
//            log("defaultPage = "+EngineAppContext.getContext().getDefaultPage());
            String exts = xml.getRootElement().
                    getElementsByTagName("handledExtensions").item(0).
                    getFirstChild().getTextContent();
            StringTokenizer st = new StringTokenizer(exts, ",");
            for (; st.hasMoreTokens(); ) {
                ThrithonAppContext.getContext().getExtensions().add(st.nextToken());
            }
            NodeList nl = xml.getRootElement().getElementsByTagName("templateDir");
            if(nl.getLength()>0){
                ThrithonAppContext.getContext().setTemplateDir(nl.item(0).getFirstChild().getTextContent());
            }
            nl = xml.getRootElement().getElementsByTagName("beans");
            if(nl.getLength()>0){
                nl = nl.item(0).getChildNodes();
                for (int i = 0; i < nl.getLength(); i++) {
                    if(!(nl.item(i) instanceof Element))
                        continue;
                    Element n = (Element) nl.item(i);
                    BeanDefinition bean = new BeanDefinition();
                    bean.setAlias(n.getAttribute("name"));
                    bean.setClassName(n.getAttribute("class"));
                    bean.setScope(n.getAttribute("scope"));
                    ThrithonAppContext.getContext().getBeans().add(bean);
                }
            }
            nl = xml.getRootElement().getElementsByTagName("actions");
            if(nl.getLength()>0){
                nl = nl.item(0).getChildNodes();
                for (int i = 0; i < nl.getLength(); i++) {
                    if(!(nl.item(i) instanceof Element))
                        continue;
                    Element n = (Element) nl.item(i);
                    ActionDefinition action = new ActionDefinition();
                    action.setAlias(n.getAttribute("name"));
                    action.setMethod(n.getAttribute("method"));
                    ThrithonAppContext.getContext().getActions().add(action);
                }
            }
            
            nl = xml.getRootElement().getElementsByTagName("filters");
            if(nl.getLength()>0){
                nl = nl.item(0).getChildNodes();
                for (int i = 0; i < nl.getLength(); i++) {
                    if(!(nl.item(i) instanceof Element))
                        continue;
                    Element n = (Element) nl.item(i);
                    try {
                        Class _class = Class.forName(n.getAttribute("class"));
                        FilterDefinition filter = new FilterDefinition();
                        filter.setAlias(n.getAttribute("name"));
                        filter.setBeforeAction("true".equals(n.getAttribute("before_action")));
                        filter.setFilter((ThrithonFilter) _class.newInstance());
                        ThrithonAppContext.getContext().getFilters().add(filter);
                    } catch (IllegalAccessException illegalAccessException) {
                        illegalAccessException.printStackTrace();
                        ThrithonAppContext.log(illegalAccessException.toString());
                    }
                }
            }
            FilterDefinition filter = new FilterDefinition();
            filter.setAlias("tmpl");
            filter.setFilter(new TemplateFilter());
            ThrithonAppContext.getContext().getFilters().add(filter);
        } catch (Exception ex) {
            ex.printStackTrace();
            ThrithonAppContext.log(ex.toString());
        }
    }

    private void sendProcessingError(Throwable t, ServletResponse response) {
	
	String stackTrace = getStackTrace(t); 

	if(stackTrace != null && !stackTrace.equals("")) {

	    try {
		    
		response.setContentType("text/html");
		PrintStream ps = new PrintStream(response.getOutputStream());
		PrintWriter pw = new PrintWriter(ps); 
		pw.print("<html>\n<head>\n<title>Error</title>\n</head>\n<body>\n"); //NOI18N
		    
		// PENDING! Localize this for next official release
		pw.print("<h1>The resource did not process correctly</h1>\n<pre>\n"); 
		pw.print(stackTrace); 
		pw.print("</pre></body>\n</html>"); //NOI18N
		pw.close();
		ps.close();
		response.getOutputStream().close();;
	    }
		
	    catch(Exception ex){ }
	}
	else {
	    try {
		PrintStream ps = new PrintStream(response.getOutputStream());
		t.printStackTrace(ps);
		ps.close();
		response.getOutputStream().close();;
	    }
	    catch(Exception ex){ }
	}
    }

    public static String getStackTrace(Throwable t) {

	String stackTrace = null;
	try {
	    StringWriter sw = new StringWriter();
	    PrintWriter pw = new PrintWriter(sw);
	    t.printStackTrace(pw);
	    pw.close();
	    sw.close();
	    stackTrace = sw.getBuffer().toString();
	}
	catch(Exception ex) {}
	return stackTrace;
    }

    private static final boolean debug = true;
}